
#include <glad/glad.h>
/* glad 必须在 include<glfw3>之前
https://learnopengl.com/Getting-started/Hello-Window

Be sure to include GLAD before GLFW.
The include file for GLAD includes the required OpenGL headers behind the scenes
(like GL/gl.h) so be sure to include GLAD before other header files that require
OpenGL (like GLFW).
*/
// #include <KHR/khrplatform.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <GLFW/glfw3.h>
#include <cstddef>
#include <iostream>
#include "CoordinateSystemShader.h"
#include "EboRectangleRenderer.h"
#include "InOutShader.h"
#include "MatrixTransformationShader.h"
#include "Renderer.h"
#include "SimpleTriangle.h"
#include "TextureProxyShader.h"
#include "UniformShader.h"
#include "VerticesColorTriangle.h"
#include "WoodenTextureShader.h"
#include "More3D.h"


using namespace std;

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{

    glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        // 键盘上 esc 被按下
        // 下一次主循环中 glfwWindowShouldClose() 被调用时就会退出循环
        glfwSetWindowShouldClose(window, true);
    }
}

int main()
{

    glfwInit();
    // 所有 GLFW 的可配置选项在
    // https://www.glfw.org/docs/latest/window.html#window_hints
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(800, 600, "learn openGL", NULL, NULL);

    if (window == NULL)
    {
        cout << "Failed to create GLFW window" << endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    // initialize GLAD
    /*
    We pass GLAD the function to load the address of the OpenGL function
    pointers which is OS-specific. GLFW gives us glfwGetProcAddress that defines
    the correct function based on which OS we're compiling for.
    */
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        cout << "Failed to initialize GLAD" << endl;
        return -1;
    }

    /*
    Before we can start rendering we have to do one last thing.
     We have to tell OpenGL the size of the rendering window
     so OpenGL knows how we want to display the data and coordinates with
    respect to the window. We can set those dimensions via the glViewport
    function

    可以把 Normalized Device Coordinates (NDC) 转换为 screen-space coordinates
    坐标空间

    */
    glViewport(0, 0, 800, 600);

    /*
        当用户改变窗口大小时，自定义的回调函数被调用
    */
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // 分配栈对象
    auto renderer = More3D(window);// WoodenTextureShader(); //TextureProxyShader();//

    auto shader = renderer;//CoordinateSystemShader();// 
    //  // InOutShader();
    // MatrixTransformationShader();//InOutShader();

    auto shaderProgram = shader.createShaderProgram();

    /**因为每一次在render loop 中进行渲染时，都需要准备渲染环境，比较繁琐
        为了简化操作创建VAO对象，VAO绑定了整个繁琐的步骤
    */

    unsigned int VAO;
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    renderer.prepareVAO();

    while (!glfwWindowShouldClose(window))
    {

        // 处理输入事件
        processInput(window);

        /*
        the glClearColor function is a state-setting function

        Note that we also specify the color to clear the screen with using
        glClearColor. Whenever we call glClear and clear the color buffer, the
        entire color buffer will be filled with the color as configured by
        glClearColor
        */
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

        /*
        We can clear the screen's color buffer using glClear where we pass in
        buffer bits to specify which buffer we would like to clear. The possible
        bits we can set are GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT and
        GL_STENCIL_BUFFER_BIT
        传入  buffer bits 来指定要清除哪一个buffer0
         glClear is a state-using function in that it uses the current state to
        retrieve the clearing color from.
        */
        glClear(GL_COLOR_BUFFER_BIT);

        /*// bind textures on corresponding texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, shader.texture1);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, shader.texture2);
*/
        glUseProgram(shaderProgram);

        /**
           本来每渲染一帧，都需要
           1、glEnableVertexAttribArray or glDisableVertexAttribArray.
           2、顶点属性配置 glVertexAttribPointer.
           3、通过 glVertexAttribPointer 匹配 VBO 和 vertex attributes
           但是有了VAO之后，
           上面三个步骤的状态都能记录在VAO中，这样每次渲染的时候只需要
           绑定VAO就能省去上面三个步骤
       */
        glBindVertexArray(VAO);

        shader.update();
        renderer.render();
        glBindVertexArray(0); // 解绑 VAO
        /**
        The glfwSwapBuffers will swap the color buffer (a large 2D buffer that
        contains color values for each pixel in GLFW's window) that is used to
        render to during this render iteration and show it as output to the
        screen.
        */
        glfwSwapBuffers(window);

        glfwPollEvents();
    }

    glfwTerminate();

    return 0;
}